﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Publicuse.Util
{
    /// <summary>
    /// DTW算法（动态时间规整）
    /// 参考：https://www.jianshu.com/p/ad9fb4b487f8
    /// </summary>
    public class DTWUtil
    {
        private readonly int _length = 10;
        /// <summary>
        /// 将两个经纬度组成线划分成10个值
        /// </summary>
        /// <param name="slat"></param>
        /// <param name="slng"></param>
        /// <param name="elat"></param>
        /// <param name="elng"></param>
        /// <returns></returns>
        private List<double> GetValueList(double slat, double slng, double elat, double elng)
        {
            List<double> list = new List<double>();

            //设置成整数方便计算
            slat *= 100000;
            slng *= 100000;
            elat *= 100000;
            elng *= 100000;
            //纬度宽
            double width = slat - elat;
            //经度高
            double height = slng - elng;

            //宽间隔
            double winterval = width / 1.0 / _length;
            //高间隔
            double hinterval = height / 1.0 / _length;


            //for (int i = 0; i < _length; i++)
            //{
            //    list.Add(Math.Atan2(slat, slng));
            //    slat += winterval;
            //    slng += hinterval;
            //}

            double lat_temp = 1;
            double lng_temp = 1;
            list.Add(Math.Atan2(slat, slng));
            for (int i = 0; i < _length; i++)
            {
                lat_temp += winterval;
                lng_temp += hinterval;
                list.Add(Math.Atan2(lat_temp, lng_temp));

            }
            return list;
        }
        /// <summary>
        /// 计算两组值的相似度
        /// </summary>
        /// <param name="fValue"></param>
        /// <param name="sValue"></param>
        /// <returns></returns>
        private double GetDistance(List<double> fValue, List<double> sValue)
        {
            //创建一个二维数组，并初始化为最大值
            double[,] cost = new double[_length, _length];
            for (int i = 0; i < _length; i++)
            {
                for (int j = 0; j < _length; j++)
                {
                    cost[i, j] = double.MaxValue;
                }
            }
            //设置00值
            cost[0, 0] = Math.Abs(fValue[0] - sValue[0]);
            for (int i = 1; i < _length; i++)
            {
                cost[i, 0] = cost[i - 1, 0] + Math.Abs(fValue[i] - sValue[0]);
            }

            for (int j = 1; j < _length; j++)
            {
                cost[0, j] = cost[0, j - 1] + Math.Abs(fValue[0] - sValue[j]);
            }

            for (int i = 1; i < _length; i++)
            {
                for (int j = 1; j < _length; j++)
                {
                    double temp = Math.Min(cost[i - 1, j - 1], cost[i, j - 1]);
                    temp = Math.Min(temp, cost[i - 1, j]);
                    cost[i, j] = temp + Math.Abs(fValue[i] - sValue[j]);
                }
            }
            return cost[_length - 1, _length - 1];
        }
        /// <summary>
        /// DTW算法
        /// 返回差异度，值越低相似度越大
        /// </summary>
        /// <param name="fPoint">第一条线的起点终点</param>
        /// <param name="sPoint">第二条线的起点终点</param>
        public double GetSimilarity(List<double> fPoint, List<double> sPoint)
        {
            List<double> fValue = GetValueList(fPoint[0], fPoint[1], fPoint[2], fPoint[3]);
            List<double> sValue = GetValueList(sPoint[0], sPoint[1], sPoint[2], sPoint[3]);
            return GetDistance(fValue, sValue);
        }

    }
}
